#pragma once
#include "CGIPrefix.h"
#include "Runtime/Base/Object.h"
#include "Runtime/Math/IntPoint.h"
#include "Runtime/Math/IntVector.h"
#include "Runtime/Math/Color.h"

namespace Alice{
	class CGICommandListImmediate;
	enum class EConservativeRasterization : uint8
	{
		Disabled,
		Overestimated,
	};
	enum class EClearBinding
	{
		ENoneBound, //no clear color associated with this target.  Target will not do hardware clears on most platforms
		EColorBound, //target has a clear color bound.  Clears will use the bound color, and do hardware clears.
		EDepthStencilBound, //target has a depthstencil value bound.  Clears will use the bound values and do hardware clears.
	};
	VkFormat UEToVkBufferFormat(EVertexElementType Type);
	// Defines which channel is valid for each pixel format
	enum class EPixelFormatChannelFlags : uint8
	{
		R = 1 << 0,
		G = 1 << 1,
		B = 1 << 2,
		A = 1 << 3,
		RG = R | G,
		RGB = R | G | B,
		RGBA = R | G | B | A,

		None = 0,
	};
	ENUM_CLASS_FLAGS(EPixelFormatChannelFlags);
	// Hints for some RHIs that support subpasses
	enum class ESubpassHint : uint8
	{
		// Regular rendering
		None,

		// Render pass has depth reading subpass
		DepthReadSubpass,

		// Mobile defferred shading subpass
		DeferredShadingSubpass,

		// Mobile MSAA custom resolve subpass. Includes DepthReadSubpass.
		CustomResolveSubpass,
	};
	enum class EPixelFormatCapabilities : uint32
	{
		None             = 0,
		Texture1D        = 1ull << 1,
		Texture2D        = 1ull << 2,
		Texture3D        = 1ull << 3,
		TextureCube      = 1ull << 4,
		RenderTarget     = 1ull << 5,
		DepthStencil     = 1ull << 6,
		TextureMipmaps   = 1ull << 7,
		TextureLoad      = 1ull << 8,
		TextureSample    = 1ull << 9,
		TextureGather    = 1ull << 10,
		TextureAtomics   = 1ull << 11,
		TextureBlendable = 1ull << 12,
		TextureStore     = 1ull << 13,

		Buffer           = 1ull << 14,
		VertexBuffer     = 1ull << 15,
		IndexBuffer      = 1ull << 16,
		BufferLoad       = 1ull << 17,
		BufferStore      = 1ull << 18,
		BufferAtomics    = 1ull << 19,

		UAV              = 1ull << 20,
		TypedUAVLoad     = 1ull << 21,
		TypedUAVStore    = 1ull << 22,

		TextureFilterable = 1ull << 23,

		AnyTexture       = Texture1D | Texture2D | Texture3D | TextureCube,

		AllTextureFlags  = AnyTexture | RenderTarget | DepthStencil | TextureMipmaps | TextureLoad | TextureSample | TextureGather | TextureAtomics | TextureBlendable | TextureStore,
		AllBufferFlags   = Buffer | VertexBuffer | IndexBuffer | BufferLoad | BufferStore | BufferAtomics,
		AllUAVFlags      = UAV | TypedUAVLoad | TypedUAVStore,

		AllFlags         = AllTextureFlags | AllBufferFlags | AllUAVFlags
	};
	ENUM_CLASS_FLAGS(EPixelFormatCapabilities);
	/** 
	* Information about a pixel format. The majority of this structure is valid after static init, however RHI does keep
	* some state in here that is initialized by that module and should not be used by general use programs that don't
	* have RHI (so noted in comments).
	*/
	struct FPixelFormatInfo
	{
		FPixelFormatInfo() = delete;
		FPixelFormatInfo(
			EPixelFormat InUnrealFormat,
			const TCHAR* InName,
			int32 InBlockSizeX,
			int32 InBlockSizeY,
			int32 InBlockSizeZ,
			int32 InBlockBytes,
			int32 InNumComponents,
			bool  InSupported);

		const TCHAR*				Name;
		EPixelFormat				UnrealFormat;
		int32						BlockSizeX;
		int32						BlockSizeY;
		int32						BlockSizeZ;
		int32						BlockBytes;
		int32						NumComponents;

		/** Per platform cabilities for the format (initialized by RHI module - invalid otherwise) */
		EPixelFormatCapabilities	Capabilities = EPixelFormatCapabilities::None;

		/** Platform specific converted format (initialized by RHI module - invalid otherwise) */
		uint32						PlatformFormat{ 0 };

		/** Whether the texture format is supported on the current platform/ rendering combination */
		uint8						Supported : 1;

		// If false, 32 bit float is assumed (initialized by RHI module - invalid otherwise)
		uint8						bIs24BitUnormDepthStencil : 1;
		
		/** 
		* Get 2D/3D image/texture size in bytes. This is for storage of the encoded image data, and does not adjust
		* for any GPU alignment/padding constraints. It is also not valid for tiled or packed mip tails (i.e. cooked mips 
		* for consoles). Only use these when you know you're working with bog standard textures/images in block based pixel formats.
		*/
		uint64 Get2DImageSizeInBytes(uint32 InWidth, uint32 InHeight) const;
		uint64 Get2DTextureMipSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InMipIndex) const;
		uint64 Get2DTextureSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InMipCount) const;
		uint64 Get3DImageSizeInBytes(uint32 InWidth, uint32 InHeight, uint32 InDepth) const;
		uint64 Get3DTextureMipSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InTextureDepth, uint32 InMipIndex) const;
		uint64 Get3DTextureSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InTextureDepth, uint32 InMipCount) const;
		
		/**
		* Get the number of compressed blocks necessary to hold the given dimensions.
		*/
		uint64 GetBlockCountForWidth(uint32 InWidth) const;
		uint64 GetBlockCountForHeight(uint32 InHeight) const;
	};
	extern FPixelFormatInfo GPixelFormats[EPixelFormat::PF_NUM];		// Maps members of EPixelFormat to a FPixelFormatInfo describing the format.
	ECGIAccess CGIGetDefaultResourceState(ETextureCreateFlags InUsage, bool bInHasInitialData);
	struct FClearValueBinding
	{
		struct DSVAlue
		{
			float Depth;
			uint32 Stencil;
		};

		FClearValueBinding()
			: ColorBinding(EClearBinding::EColorBound)
		{
			Value.Color[0] = 0.0f;
			Value.Color[1] = 0.0f;
			Value.Color[2] = 0.0f;
			Value.Color[3] = 0.0f;
		}

		FClearValueBinding(EClearBinding NoBinding)
			: ColorBinding(NoBinding)
		{
			Value.Color[0] = 0.0f;
			Value.Color[1] = 0.0f;
			Value.Color[2] = 0.0f;
			Value.Color[3] = 0.0f;

			Value.DSValue.Depth = 0.0f;
			Value.DSValue.Stencil = 0;
		}

		explicit FClearValueBinding(const FLinearColor& InClearColor)
			: ColorBinding(EClearBinding::EColorBound)
		{
			Value.Color[0] = InClearColor.R;
			Value.Color[1] = InClearColor.G;
			Value.Color[2] = InClearColor.B;
			Value.Color[3] = InClearColor.A;
		}

		explicit FClearValueBinding(float DepthClearValue, uint32 StencilClearValue = 0)
			: ColorBinding(EClearBinding::EDepthStencilBound)
		{
			Value.DSValue.Depth = DepthClearValue;
			Value.DSValue.Stencil = StencilClearValue;
		}

		FLinearColor GetClearColor() const
		{
			return FLinearColor(Value.Color[0], Value.Color[1], Value.Color[2], Value.Color[3]);
		}

		void GetDepthStencil(float& OutDepth, uint32& OutStencil) const
		{
			OutDepth = Value.DSValue.Depth;
			OutStencil = Value.DSValue.Stencil;
		}

		bool operator==(const FClearValueBinding& Other) const
		{
			if (ColorBinding == Other.ColorBinding)
			{
				if (ColorBinding == EClearBinding::EColorBound)
				{
					return
						Value.Color[0] == Other.Value.Color[0] &&
						Value.Color[1] == Other.Value.Color[1] &&
						Value.Color[2] == Other.Value.Color[2] &&
						Value.Color[3] == Other.Value.Color[3];

				}
				if (ColorBinding == EClearBinding::EDepthStencilBound)
				{
					return
						Value.DSValue.Depth == Other.Value.DSValue.Depth &&
						Value.DSValue.Stencil == Other.Value.DSValue.Stencil;
				}
				return true;
			}
			return false;
		}

		EClearBinding ColorBinding;

		union ClearValueType
		{
			float Color[4];
			DSVAlue DSValue;
		} Value;

		// common clear values
		static const FClearValueBinding None;
		static const FClearValueBinding Black;
		static const FClearValueBinding BlackMaxAlpha;
		static const FClearValueBinding White;
		static const FClearValueBinding Transparent;
		static const FClearValueBinding DepthOne;
		static const FClearValueBinding DepthZero;
		static const FClearValueBinding DepthNear;
		static const FClearValueBinding DepthFar;	
		static const FClearValueBinding Green;
		static const FClearValueBinding DefaultNormal8Bit;
	};
	/** Descriptor used to create a texture resource */
	struct CGITextureDesc
	{
		CGITextureDesc() = default;

		CGITextureDesc(const CGITextureDesc& Other)
		{
			*this = Other;
		}

		CGITextureDesc(ETextureDimension InDimension)
			: Dimension(InDimension)
		{}

		CGITextureDesc(
			ETextureDimension   InDimension
			, ETextureCreateFlags InFlags
			, EPixelFormat        InFormat
			, FClearValueBinding  InClearValue
			, IntPoint            InExtent
			, uint16              InDepth
			, uint16              InArraySize
			, uint8               InNumMips
			, uint8               InNumSamples
			, uint32              InExtData
			)
			: Flags     (InFlags     )
			, ClearValue(InClearValue)
			, ExtData   (InExtData   )
			, Extent    (InExtent    )
			, Depth     (InDepth     )
			, ArraySize (InArraySize )
			, NumMips   (InNumMips   )
			, NumSamples(InNumSamples)
			, Dimension (InDimension )
			, Format    (InFormat    )
		{}

		bool operator == (const CGITextureDesc& Other) const
		{
			return Dimension  == Other.Dimension
				&& Flags      == Other.Flags
				&& Format     == Other.Format
				&& UAVFormat  == Other.UAVFormat
				&& Extent     == Other.Extent
				&& Depth      == Other.Depth
				&& ArraySize  == Other.ArraySize
				&& NumMips    == Other.NumMips
				&& NumSamples == Other.NumSamples
				&& FastVRAMPercentage == Other.FastVRAMPercentage
				&& ClearValue == Other.ClearValue
				&& ExtData    == Other.ExtData;
		}

		bool operator != (const CGITextureDesc& Other) const
		{
			return !(*this == Other);
		}

		CGITextureDesc& operator=(const CGITextureDesc& Other)
		{
			Dimension			= Other.Dimension;
			Flags				= Other.Flags;
			Format				= Other.Format;
			UAVFormat			= Other.UAVFormat;
			Extent				= Other.Extent;
			Depth				= Other.Depth;
			ArraySize			= Other.ArraySize;
			NumMips				= Other.NumMips;
			NumSamples			= Other.NumSamples;
			ClearValue			= Other.ClearValue;
			ExtData				= Other.ExtData;
			FastVRAMPercentage	= Other.FastVRAMPercentage;

			return *this;
		}

		bool IsTexture2D() const
		{
			return Dimension == ETextureDimension::Texture2D || Dimension == ETextureDimension::Texture2DArray;
		}

		bool IsTexture3D() const
		{
			return Dimension == ETextureDimension::Texture3D;
		}

		bool IsTextureCube() const
		{
			return Dimension == ETextureDimension::TextureCube || Dimension == ETextureDimension::TextureCubeArray;
		}

		bool IsTextureArray() const
		{
			return Dimension == ETextureDimension::Texture2DArray || Dimension == ETextureDimension::TextureCubeArray;
		}

		bool IsMipChain() const
		{
			return NumMips > 1;
		}

		bool IsMultisample() const
		{
			return NumSamples > 1;
		}

		IntVector GetSize() const
		{
			return IntVector(Extent.X, Extent.Y, Depth);
		}

		void Reset()
		{
			// Usually we don't want to propagate MSAA samples.
			NumSamples = 1;

			// Remove UAV flag for textures that don't need it (some formats are incompatible).
			Flags |= ETextureCreateFlags::RenderTargetable;
			Flags &= ~(ETextureCreateFlags::UAV | ETextureCreateFlags::ResolveTargetable | ETextureCreateFlags::DepthStencilResolveTarget | ETextureCreateFlags::Memoryless);
		}

		/** Returns whether this descriptor conforms to requirements. */
		bool IsValid() const
		{
			return CGITextureDesc::Validate(*this, /* Name = */ TEXT(""), /* bFatal = */ false);
		}

		/** Texture flags passed on to RHI texture. */
		ETextureCreateFlags Flags = ETextureCreateFlags::None;

		/** Clear value to use when fast-clearing the texture. */
		FClearValueBinding ClearValue;

		/** Platform-specific additional data. Used for offline processed textures on some platforms. */
		uint32 ExtData = 0;

		/** Extent of the texture in x and y. */
		IntPoint Extent = IntPoint(1, 1);

		/** Depth of the texture if the dimension is 3D. */
		uint16 Depth = 1;

		/** The number of array elements in the texture. (Keep at 1 if dimension is 3D). */
		uint16 ArraySize = 1;

		/** Number of mips in the texture mip-map chain. */
		uint8 NumMips = 1;

		/** Number of samples in the texture. >1 for MSAA. */
		uint8 NumSamples = 1;

		/** Texture dimension to use when creating the RHI texture. */
		ETextureDimension Dimension = ETextureDimension::Texture2D;

		/** Pixel format used to create RHI texture. */
		EPixelFormat Format = EPixelFormat::PF_Unknown;

		/** Texture format used when creating the UAV. PF_Unknown means to use the default one (same as Format). */
		EPixelFormat UAVFormat = EPixelFormat::PF_Unknown;

		/** Resource memory percentage which should be allocated onto fast VRAM (hint-only). (encoding into 8bits, 0..255 -> 0%..100%) */
		uint8 FastVRAMPercentage = 0xFF;

		/** Check the validity. */
		static bool CheckValidity(const CGITextureDesc& Desc, const TCHAR* Name)
		{
			return CGITextureDesc::Validate(Desc, Name, /* bFatal = */ true);
		}

		/**
		 * Returns an estimated total memory size the described texture will occupy in GPU memory.
		 * This is an estimate because it only considers the dimensions / format etc of the texture, 
		 * not any specifics about platform texture layout.
		 * 
		 * To get a true measure of a texture resource for the current running platform RHI, use RHICalcTexturePlatformSize().
		 * 
		 * @param FirstMipIndex - the index of the most detailed mip to consider in the memory size calculation. Must be < NumMips and <= LastMipIndex.
		 * @param LastMipIndex  - the index of the least detailed mip to consider in the memory size calculation. Must be < NumMips and >= FirstMipIndex.
		 */
		uint64 CalcMemorySizeEstimate(uint32 FirstMipIndex, uint32 LastMipIndex) const;

		uint64 CalcMemorySizeEstimate(uint32 FirstMipIndex = 0) const
		{
			return CalcMemorySizeEstimate(FirstMipIndex, NumMips - 1);
		}

	private:
		static bool Validate(const CGITextureDesc& Desc, const TCHAR* Name, bool bFatal);
	};
	struct CGITextureCreateDesc : public CGITextureDesc
	{
		static CGITextureCreateDesc Create(const TCHAR* InDebugName, ETextureDimension InDimension)
		{
			return CGITextureCreateDesc(InDimension);
		}

		static CGITextureCreateDesc Create2D(const TCHAR* InDebugName)
		{
			return CGITextureCreateDesc(ETextureDimension::Texture2D);
		}

		static CGITextureCreateDesc Create2DArray(const TCHAR* InDebugName)
		{
			return CGITextureCreateDesc(ETextureDimension::Texture2DArray);
		}

		static CGITextureCreateDesc Create3D(const TCHAR* InDebugName)
		{
			return CGITextureCreateDesc(ETextureDimension::Texture3D);
		}

		static CGITextureCreateDesc CreateCube(const TCHAR* InDebugName)
		{
			return CGITextureCreateDesc(ETextureDimension::TextureCube);
		}

		static CGITextureCreateDesc CreateCubeArray(const TCHAR* InDebugName)
		{
			return CGITextureCreateDesc(ETextureDimension::TextureCubeArray);
		}

		static CGITextureCreateDesc Create2D(const TCHAR* DebugName, IntPoint Size, EPixelFormat Format)
		{
			return Create2D(DebugName)
				.SetExtent(Size)
				.SetFormat(Format);
		}

		static CGITextureCreateDesc Create2D(const TCHAR* DebugName, int32 SizeX, int32 SizeY, EPixelFormat Format)
		{
			return Create2D(DebugName)
				.SetExtent(SizeX, SizeY)
				.SetFormat(Format);
		}

		static CGITextureCreateDesc Create2DArray(const TCHAR* DebugName, IntPoint Size, uint16 ArraySize, EPixelFormat Format)
		{
			return Create2DArray(DebugName)
				.SetExtent(Size)
				.SetFormat(Format)
				.SetArraySize((uint16)ArraySize);
		}

		static CGITextureCreateDesc Create2DArray(const TCHAR* DebugName, int32 SizeX, int32 SizeY, int32 ArraySize, EPixelFormat Format)
		{
			return Create2DArray(DebugName)
				.SetExtent(SizeX, SizeY)
				.SetFormat(Format)
				.SetArraySize((uint16)ArraySize);
		}

		static CGITextureCreateDesc Create3D(const TCHAR* DebugName, IntVector Size, EPixelFormat Format)
		{
			return Create3D(DebugName)
				.SetExtent(Size.X, Size.Y)
				.SetDepth((uint16)Size.Z)
				.SetFormat(Format);
		}

		static CGITextureCreateDesc Create3D(const TCHAR* DebugName, int32 SizeX, int32 SizeY, int32 SizeZ, EPixelFormat Format)
		{
			return Create3D(DebugName)
				.SetExtent(SizeX, SizeY)
				.SetDepth((uint16)SizeZ)
				.SetFormat(Format);
		}

		static CGITextureCreateDesc CreateCube(const TCHAR* DebugName, uint32 Size, EPixelFormat Format)
		{
			return CreateCube(DebugName)
				.SetExtent(Size)
				.SetFormat(Format);
		}

		static CGITextureCreateDesc CreateCubeArray(const TCHAR* DebugName, uint32 Size, uint16 ArraySize, EPixelFormat Format)
		{
			return CreateCubeArray(DebugName)
				.SetExtent(Size)
				.SetFormat(Format)
				.SetArraySize((uint16)ArraySize);
		}

		CGITextureCreateDesc() = default;

		// Constructor with minimal argument set. Name and dimension are always required.
		CGITextureCreateDesc(ETextureDimension InDimension)
			: CGITextureDesc(InDimension)
		{
		}
		CGITextureCreateDesc& SetFlags(ETextureCreateFlags InFlags)               { Flags = InFlags;                          return *this; }
		CGITextureCreateDesc& AddFlags(ETextureCreateFlags InFlags)               { Flags |= InFlags;                         return *this; }
		CGITextureCreateDesc& SetClearValue(FClearValueBinding InClearValue)      { ClearValue = InClearValue;                return *this; }
		CGITextureCreateDesc& SetExtData(uint32 InExtData)                        { ExtData = InExtData;                      return *this; }
		CGITextureCreateDesc& SetExtent(const IntPoint& InExtent)                { Extent = InExtent;                        return *this; }
		CGITextureCreateDesc& SetExtent(int32 InExtentX, int32 InExtentY)         { Extent = IntPoint(InExtentX, InExtentY); return *this; }
		CGITextureCreateDesc& SetExtent(uint32 InExtent)                          { Extent = IntPoint(InExtent);             return *this; }
		CGITextureCreateDesc& SetDepth(uint16 InDepth)                            { Depth = InDepth;                          return *this; }
		CGITextureCreateDesc& SetArraySize(uint16 InArraySize)                    { ArraySize = InArraySize;                  return *this; }
		CGITextureCreateDesc& SetNumMips(uint8 InNumMips)                         { NumMips = InNumMips;                      return *this; }
		CGITextureCreateDesc& SetNumSamples(uint8 InNumSamples)                   { NumSamples = InNumSamples;                return *this; }
		CGITextureCreateDesc& SetDimension(ETextureDimension InDimension)         { Dimension = InDimension;                  return *this; }
		CGITextureCreateDesc& SetFormat(EPixelFormat InFormat)                    { Format = InFormat;                        return *this; }
		CGITextureCreateDesc& SetUAVFormat(EPixelFormat InUAVFormat)              { UAVFormat = InUAVFormat;                  return *this; }
		CGITextureCreateDesc& SetInitialState(ECGIAccess InInitialState)          { InitialState = InInitialState;            return *this; }
		CGITextureCreateDesc& DetermineInititialState()                           { if (InitialState == ECGIAccess::Unknown) InitialState = CGIGetDefaultResourceState(Flags, false); return *this; }
		CGITextureCreateDesc& SetFastVRAMPercentage(float In)                     { FastVRAMPercentage = uint8(MathUtils::Clamp(In, 0.f, 1.0f) * 0xFF); return *this; }
		CGITextureCreateDesc& SetClassName(const std::string& InClassName)			  { ClassName = InClassName;				   return *this; }
		CGITextureCreateDesc& SetOwnerName(const std::string& InOwnerName)			{ OwnerName = InOwnerName;                  return *this; }

		/* The RHI access state that the resource will be created in. */
		ECGIAccess InitialState = ECGIAccess::Unknown;

		std::string ClassName;	// The owner class of FRHITexture used for Insight asset metadata tracing
		std::string OwnerName;	// The owner name used for Insight asset metadata tracing
	};
	class FResourceArrayUploadInterface;
	struct CGIResourceCreateInfo
	{
		CGIResourceCreateInfo(const TCHAR* InDebugName)
			: DebugName(InDebugName)
		{
		}
		// for CreateBuffer calls
		CGIResourceCreateInfo(const TCHAR* InDebugName, FResourceArrayUploadInterface* InResourceArray)
			: CGIResourceCreateInfo(InDebugName)
		{
			ResourceArray = InResourceArray;
		}
		// for CreateBuffer calls
		FResourceArrayUploadInterface* ResourceArray = nullptr;
		// whether to create an RHI object with no underlying resource
		bool bWithoutNativeResource = false;
		const TCHAR* DebugName;
		const TCHAR* ClassName;
		const TCHAR*  OwnerName;
	};
	struct CGIBufferDesc
	{
		uint32 Size{};
		uint32 Stride{};
		EBufferUsageFlags Usage{};

		CGIBufferDesc() = default;
		CGIBufferDesc(uint32 InSize, uint32 InStride, EBufferUsageFlags InUsage)
			: Size  (InSize)
			, Stride(InStride)
			, Usage (InUsage)
		{}

		static CGIBufferDesc Null()
		{
			return CGIBufferDesc(0, 0, EBufferUsageFlags::NullResource);
		}

		bool IsNull() const
		{
			if (EnumHasAnyFlags(Usage, EBufferUsageFlags::NullResource))
			{
				// The null resource descriptor should have its other fields zeroed, and no additional flags.
				//check(Size == 0 && Stride == 0 && Usage == BUF_NullResource);
				return true;
			}

			return false;
		}
	};
    class CGIResource : public Object{
    public:
        CGIResource(ECGIResourceType inResourceType);
        virtual ~CGIResource();
        const ECGIResourceType mResourceType;
        uint8 mbCommitted : 1;
        uint8 mbAllowExtendLifetime : 1;
    };
	class CGIVertexDeclaration : public CGIResource
	{
	public:
		CGIVertexDeclaration() : CGIResource(RRT_VertexDeclaration) {}
	};
	enum class ERHIDescriptorHeapType : uint8
	{
		Standard,
		Sampler,
		RenderTarget,
		DepthStencil,
		Count,
		Invalid = MAX_uint8
	};

	struct FRHIDescriptorHandle
	{
		FRHIDescriptorHandle() = default;
		FRHIDescriptorHandle(ERHIDescriptorHeapType InType, uint32 InIndex)
			: Index(InIndex)
			, Type((uint8)InType)
		{
		}
		FRHIDescriptorHandle(uint8 InType, uint32 InIndex)
			: Index(InIndex)
			, Type(InType)
		{
		}

		inline uint32                 GetIndex() const { return Index; }
		inline ERHIDescriptorHeapType GetType() const { return (ERHIDescriptorHeapType)Type; }
		inline uint8                  GetRawType() const { return Type; }

		inline bool IsValid() const { return Index != MAX_uint32 && Type != (uint8)ERHIDescriptorHeapType::Invalid; }

	private:
		uint32    Index{ MAX_uint32 };
		uint8     Type{ (uint8)ERHIDescriptorHeapType::Invalid };
	};
	class CGISamplerState : public CGIResource 
	{
	public:
		CGISamplerState() : CGIResource(RRT_SamplerState) {}
		virtual bool IsImmutable() const { return false; }
		virtual FRHIDescriptorHandle GetBindlessHandle() const { return FRHIDescriptorHandle(); }
	};
	/** Maximum number of immutable samplers in a PSO. */
	enum
	{
		MaxImmutableSamplers = 2
	};

	struct FImmutableSamplerState
	{
		FImmutableSamplerState() : ImmutableSamplers(MaxImmutableSamplers, nullptr)
		{}

		void Reset()
		{
			for (uint32 Index = 0; Index < MaxImmutableSamplers; ++Index)
			{
				ImmutableSamplers[Index] = nullptr;
			}
		}

		bool operator==(const FImmutableSamplerState& rhs) const
		{
			return ImmutableSamplers == rhs.ImmutableSamplers;
		}

		bool operator!=(const FImmutableSamplerState& rhs) const
		{
			return ImmutableSamplers != rhs.ImmutableSamplers;
		}

		std::vector<CGISamplerState*> ImmutableSamplers;
	};
	class CGIRasterizerState : public CGIResource
	{
	public:
		CGIRasterizerState() : CGIResource(RRT_RasterizerState) {}
		virtual bool GetInitializer(struct FRasterizerStateInitializerRHI& Init) { return false; }
	};

	class CGIDepthStencilState : public CGIResource
	{
	public:
		CGIDepthStencilState() : CGIResource(RRT_DepthStencilState) {}
#if ENABLE_RHI_VALIDATION
		FExclusiveDepthStencil ActualDSMode;
#endif
		virtual bool GetInitializer(struct FDepthStencilStateInitializerRHI& Init) { return false; }
	};
	class CGIBlendState : public CGIResource
	{
	public:
		CGIBlendState() : CGIResource(RRT_BlendState) {}
		virtual bool GetInitializer(class FBlendStateInitializerRHI& Init) { return false; }
	};
    class CGIViewableResource : public CGIResource{
    public:
		ECGIAccess TrackedAccess;
    	CGIViewableResource(ECGIResourceType InResourceType, ECGIAccess InAccess)
			: CGIResource(InResourceType)
			, TrackedAccess(InAccess)
    	{}
    };
    class CGITexture : public CGIViewableResource{
    public:
		CGITextureDesc TextureDesc;
    	CGITexture(ECGIResourceType inResourceType):CGIViewableResource(inResourceType,ECGIAccess::Unknown){}
    	CGITexture(const CGITextureCreateDesc& InDesc);
		virtual const CGITextureDesc& GetDesc() const { return TextureDesc; }
    	uint32 GetNumMips() const { return GetDesc().NumMips; }
    };
	enum class ERenderTargetActions : uint8
	{
		LoadOpMask = 2,

	#define RTACTION_MAKE_MASK(Load, Store) (((uint8)ERenderTargetLoadAction::Load << (uint8)LoadOpMask) | (uint8)ERenderTargetStoreAction::Store)

		DontLoad_DontStore =	RTACTION_MAKE_MASK(ENoAction, ENoAction),

		DontLoad_Store =		RTACTION_MAKE_MASK(ENoAction, EStore),
		Clear_Store =			RTACTION_MAKE_MASK(EClear, EStore),
		Load_Store =			RTACTION_MAKE_MASK(ELoad, EStore),

		Clear_DontStore =		RTACTION_MAKE_MASK(EClear, ENoAction),
		Load_DontStore =		RTACTION_MAKE_MASK(ELoad, ENoAction),
		Clear_Resolve =			RTACTION_MAKE_MASK(EClear, EMultisampleResolve),
		Load_Resolve =			RTACTION_MAKE_MASK(ELoad, EMultisampleResolve),

	#undef RTACTION_MAKE_MASK
	};
	inline ERenderTargetLoadAction GetLoadAction(ERenderTargetActions Action)
	{
		return (ERenderTargetLoadAction)((uint8)Action >> (uint8)ERenderTargetActions::LoadOpMask);
	}

	inline ERenderTargetStoreAction GetStoreAction(ERenderTargetActions Action)
	{
		return (ERenderTargetStoreAction)((uint8)Action & ((1 << (uint8)ERenderTargetActions::LoadOpMask) - 1));
	}
	enum class EDepthStencilTargetActions : uint8
	{
		DepthMask = 4,

	#define RTACTION_MAKE_MASK(Depth, Stencil) (((uint8)ERenderTargetActions::Depth << (uint8)DepthMask) | (uint8)ERenderTargetActions::Stencil)

		DontLoad_DontStore =						RTACTION_MAKE_MASK(DontLoad_DontStore, DontLoad_DontStore),
		DontLoad_StoreDepthStencil =				RTACTION_MAKE_MASK(DontLoad_Store, DontLoad_Store),
		DontLoad_StoreStencilNotDepth =				RTACTION_MAKE_MASK(DontLoad_DontStore, DontLoad_Store),
		ClearDepthStencil_StoreDepthStencil =		RTACTION_MAKE_MASK(Clear_Store, Clear_Store),
		LoadDepthStencil_StoreDepthStencil =		RTACTION_MAKE_MASK(Load_Store, Load_Store),
		LoadDepthNotStencil_StoreDepthNotStencil =	RTACTION_MAKE_MASK(Load_Store, DontLoad_DontStore),
		LoadDepthNotStencil_DontStore =				RTACTION_MAKE_MASK(Load_DontStore, DontLoad_DontStore),
		LoadDepthStencil_StoreStencilNotDepth =		RTACTION_MAKE_MASK(Load_DontStore, Load_Store),

		ClearDepthStencil_DontStoreDepthStencil =	RTACTION_MAKE_MASK(Clear_DontStore, Clear_DontStore),
		LoadDepthStencil_DontStoreDepthStencil =	RTACTION_MAKE_MASK(Load_DontStore, Load_DontStore),
		ClearDepthStencil_StoreDepthNotStencil =	RTACTION_MAKE_MASK(Clear_Store, Clear_DontStore),
		ClearDepthStencil_StoreStencilNotDepth =	RTACTION_MAKE_MASK(Clear_DontStore, Clear_Store),
		ClearDepthStencil_ResolveDepthNotStencil =	RTACTION_MAKE_MASK(Clear_Resolve, Clear_DontStore),
		ClearDepthStencil_ResolveStencilNotDepth =	RTACTION_MAKE_MASK(Clear_DontStore, Clear_Resolve),
		LoadDepthClearStencil_StoreDepthStencil  =  RTACTION_MAKE_MASK(Load_Store, Clear_Store),

		ClearStencilDontLoadDepth_StoreStencilNotDepth = RTACTION_MAKE_MASK(DontLoad_DontStore, Clear_Store),

	#undef RTACTION_MAKE_MASK
	};
	class ExclusiveDepthStencil
	{
	public:
		enum Type
		{
			// don't use those directly, use the combined versions below
			// 4 bits are used for depth and 4 for stencil to make the hex value readable and non overlapping
			DepthNop = 0x00,
			DepthRead = 0x01,
			DepthWrite = 0x02,
			DepthMask = 0x0f,
			StencilNop = 0x00,
			StencilRead = 0x10,
			StencilWrite = 0x20,
			StencilMask = 0xf0,

			// use those:
			DepthNop_StencilNop = DepthNop + StencilNop,
			DepthRead_StencilNop = DepthRead + StencilNop,
			DepthWrite_StencilNop = DepthWrite + StencilNop,
			DepthNop_StencilRead = DepthNop + StencilRead,
			DepthRead_StencilRead = DepthRead + StencilRead,
			DepthWrite_StencilRead = DepthWrite + StencilRead,
			DepthNop_StencilWrite = DepthNop + StencilWrite,
			DepthRead_StencilWrite = DepthRead + StencilWrite,
			DepthWrite_StencilWrite = DepthWrite + StencilWrite,
		};

	private:
		Type Value;

	public:
		// constructor
		ExclusiveDepthStencil(Type InValue = DepthNop_StencilNop)
			: Value(InValue)
		{
		}

		inline bool IsUsingDepthStencil() const
		{
			return Value != DepthNop_StencilNop;
		}
		inline bool IsUsingDepth() const
		{
			return (ExtractDepth() != DepthNop);
		}
		inline bool IsUsingStencil() const
		{
			return (ExtractStencil() != StencilNop);
		}
		inline bool IsDepthWrite() const
		{
			return ExtractDepth() == DepthWrite;
		}
		inline bool IsDepthRead() const
		{
			return ExtractDepth() == DepthRead;
		}
		inline bool IsStencilWrite() const
		{
			return ExtractStencil() == StencilWrite;
		}
		inline bool IsStencilRead() const
		{
			return ExtractStencil() == StencilRead;
		}

		inline bool IsAnyWrite() const
		{
			return IsDepthWrite() || IsStencilWrite();
		}

		inline void SetDepthWrite()
		{
			Value = (Type)(ExtractStencil() | DepthWrite);
		}
		inline void SetStencilWrite()
		{
			Value = (Type)(ExtractDepth() | StencilWrite);
		}
		inline void SetDepthStencilWrite(bool bDepth, bool bStencil)
		{
			Value = DepthNop_StencilNop;

			if (bDepth)
			{
				SetDepthWrite();
			}
			if (bStencil)
			{
				SetStencilWrite();
			}
		}
		bool operator==(const ExclusiveDepthStencil& rhs) const
		{
			return Value == rhs.Value;
		}

		bool operator != (const ExclusiveDepthStencil& RHS) const
		{
			return Value != RHS.Value;
		}

		inline bool IsValid(ExclusiveDepthStencil& Current) const
		{
			Type Depth = ExtractDepth();

			if (Depth != DepthNop && Depth != Current.ExtractDepth())
			{
				return false;
			}

			Type Stencil = ExtractStencil();

			if (Stencil != StencilNop && Stencil != Current.ExtractStencil())
			{
				return false;
			}

			return true;
		}

		inline void GetAccess(ECGIAccess& DepthAccess, ECGIAccess& StencilAccess) const
		{
			DepthAccess = ECGIAccess::None;

			// SRV access is allowed whilst a depth stencil target is "readable".
			constexpr ECGIAccess DSVReadOnlyMask =
				ECGIAccess::DSVRead;

			// If write access is required, only the depth block can access the resource.
			constexpr ECGIAccess DSVReadWriteMask =
				ECGIAccess::DSVRead |
				ECGIAccess::DSVWrite;

			if (IsUsingDepth())
			{
				DepthAccess = IsDepthWrite() ? DSVReadWriteMask : DSVReadOnlyMask;
			}

			StencilAccess = ECGIAccess::None;

			if (IsUsingStencil())
			{
				StencilAccess = IsStencilWrite() ? DSVReadWriteMask : DSVReadOnlyMask;
			}
		}

		template <typename TFunction>
		inline void EnumerateSubresources(TFunction Function) const
		{
			if (!IsUsingDepthStencil())
			{
				return;
			}

			ECGIAccess DepthAccess = ECGIAccess::None;
			ECGIAccess StencilAccess = ECGIAccess::None;
			GetAccess(DepthAccess, StencilAccess);

			// Same depth / stencil state; single subresource.
			if (DepthAccess == StencilAccess)
			{
				Function(DepthAccess, FRHITransitionInfo::kAllSubresources);
			}
			// Separate subresources for depth / stencil.
			else
			{
				if (DepthAccess != ECGIAccess::None)
				{
					Function(DepthAccess, FRHITransitionInfo::kDepthPlaneSlice);
				}
				if (StencilAccess != ECGIAccess::None)
				{
					Function(StencilAccess, FRHITransitionInfo::kStencilPlaneSlice);
				}
			}
		}

		/**
		* Returns a new FExclusiveDepthStencil to be used to transition a depth stencil resource to readable.
		* If the depth or stencil is already in a readable state, that particular component is returned as Nop,
		* to avoid unnecessary subresource transitions.
		*/
		inline ExclusiveDepthStencil GetReadableTransition() const
		{
			ExclusiveDepthStencil::Type NewDepthState = IsDepthWrite()
				? ExclusiveDepthStencil::DepthRead
				: ExclusiveDepthStencil::DepthNop;

			ExclusiveDepthStencil::Type NewStencilState = IsStencilWrite()
				? ExclusiveDepthStencil::StencilRead
				: ExclusiveDepthStencil::StencilNop;

			return (ExclusiveDepthStencil::Type)(NewDepthState | NewStencilState);
		}

		/**
		* Returns a new FExclusiveDepthStencil to be used to transition a depth stencil resource to readable.
		* If the depth or stencil is already in a readable state, that particular component is returned as Nop,
		* to avoid unnecessary subresource transitions.
		*/
		inline ExclusiveDepthStencil GetWritableTransition() const
		{
			ExclusiveDepthStencil::Type NewDepthState = IsDepthRead()
				? ExclusiveDepthStencil::DepthWrite
				: ExclusiveDepthStencil::DepthNop;

			ExclusiveDepthStencil::Type NewStencilState = IsStencilRead()
				? ExclusiveDepthStencil::StencilWrite
				: ExclusiveDepthStencil::StencilNop;

			return (ExclusiveDepthStencil::Type)(NewDepthState | NewStencilState);
		}

		uint32 GetIndex() const
		{
			// Note: The array to index has views created in that specific order.

			// we don't care about the Nop versions so less views are needed
			// we combine Nop and Write
			switch (Value)
			{
			case DepthWrite_StencilNop:
			case DepthNop_StencilWrite:
			case DepthWrite_StencilWrite:
			case DepthNop_StencilNop:
				return 0; // old DSAT_Writable

			case DepthRead_StencilNop:
			case DepthRead_StencilWrite:
				return 1; // old DSAT_ReadOnlyDepth

			case DepthNop_StencilRead:
			case DepthWrite_StencilRead:
				return 2; // old DSAT_ReadOnlyStencil

			case DepthRead_StencilRead:
				return 3; // old DSAT_ReadOnlyDepthAndStencil
			}
			// should never happen
			return -1;
		}
		static const uint32 MaxIndex = 4;

	private:
		inline Type ExtractDepth() const
		{
			return (Type)(Value & DepthMask);
		}
		inline Type ExtractStencil() const
		{
			return (Type)(Value & StencilMask);
		}
		friend uint32 GetTypeHash(const ExclusiveDepthStencil& Ds);
	};
	struct FResolveRect
	{
		int32 X1;
		int32 Y1;
		int32 X2;
		int32 Y2;

		// e.g. for a a full 256 x 256 area starting at (0, 0) it would be 
		// the values would be 0, 0, 256, 256
		FResolveRect(int32 InX1 = -1, int32 InY1 = -1, int32 InX2 = -1, int32 InY2 = -1)
			: X1(InX1)
			, Y1(InY1)
			, X2(InX2)
			, Y2(InY2)
		{}

		bool operator==(FResolveRect Other) const
		{
			return X1 == Other.X1 && Y1 == Other.Y1 && X2 == Other.X2 && Y2 == Other.Y2;
		}

		bool operator!=(FResolveRect Other) const
		{
			return !(*this == Other);
		}

		bool IsValid() const
		{
			return X1 >= 0 && Y1 >= 0 && X2 - X1 > 0 && Y2 - Y1 > 0;
		}
	};
	class FRHIRenderTargetView
	{
	public:
		CGITexture* Texture = nullptr;
		uint32 MipIndex = 0;

		/** Array slice or texture cube face.  Only valid if texture resource was created with TexCreate_TargetArraySlicesIndependently! */
		uint32 ArraySliceIndex = -1;

		ERenderTargetLoadAction LoadAction = ERenderTargetLoadAction::ENoAction;
		ERenderTargetStoreAction StoreAction = ERenderTargetStoreAction::ENoAction;

		FRHIRenderTargetView() = default;
		FRHIRenderTargetView(FRHIRenderTargetView&&) = default;
		FRHIRenderTargetView(const FRHIRenderTargetView&) = default;
		FRHIRenderTargetView& operator=(FRHIRenderTargetView&&) = default;
		FRHIRenderTargetView& operator=(const FRHIRenderTargetView&) = default;

		//common case
		explicit FRHIRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InLoadAction) :
			Texture(InTexture),
			MipIndex(0),
			ArraySliceIndex(-1),
			LoadAction(InLoadAction),
			StoreAction(ERenderTargetStoreAction::EStore)
		{}

		//common case
		explicit FRHIRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InLoadAction, uint32 InMipIndex, uint32 InArraySliceIndex) :
			Texture(InTexture),
			MipIndex(InMipIndex),
			ArraySliceIndex(InArraySliceIndex),
			LoadAction(InLoadAction),
			StoreAction(ERenderTargetStoreAction::EStore)
		{}
	
		explicit FRHIRenderTargetView(CGITexture* InTexture, uint32 InMipIndex, uint32 InArraySliceIndex, ERenderTargetLoadAction InLoadAction, ERenderTargetStoreAction InStoreAction) :
			Texture(InTexture),
			MipIndex(InMipIndex),
			ArraySliceIndex(InArraySliceIndex),
			LoadAction(InLoadAction),
			StoreAction(InStoreAction)
		{}

		bool operator==(const FRHIRenderTargetView& Other) const
		{
			return 
				Texture == Other.Texture &&
				MipIndex == Other.MipIndex &&
				ArraySliceIndex == Other.ArraySliceIndex &&
				LoadAction == Other.LoadAction &&
				StoreAction == Other.StoreAction;
		}
	};
	class CGIDepthRenderTargetView
	{
	public:
		CGITexture* Texture;

		ERenderTargetLoadAction		DepthLoadAction;
		ERenderTargetStoreAction	DepthStoreAction;
		ERenderTargetLoadAction		StencilLoadAction;

	private:
		ERenderTargetStoreAction	StencilStoreAction;
		ExclusiveDepthStencil		DepthStencilAccess;
	public:

		// accessor to prevent write access to StencilStoreAction
		ERenderTargetStoreAction GetStencilStoreAction() const { return StencilStoreAction; }
		// accessor to prevent write access to DepthStencilAccess
		ExclusiveDepthStencil GetDepthStencilAccess() const { return DepthStencilAccess; }

		explicit CGIDepthRenderTargetView() :
			Texture(nullptr),
			DepthLoadAction(ERenderTargetLoadAction::ENoAction),
			DepthStoreAction(ERenderTargetStoreAction::ENoAction),
			StencilLoadAction(ERenderTargetLoadAction::ENoAction),
			StencilStoreAction(ERenderTargetStoreAction::ENoAction),
			DepthStencilAccess(ExclusiveDepthStencil::DepthNop_StencilNop)
		{
			Validate();
		}

		//common case
		explicit CGIDepthRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InLoadAction, ERenderTargetStoreAction InStoreAction) :
			Texture(InTexture),
			DepthLoadAction(InLoadAction),
			DepthStoreAction(InStoreAction),
			StencilLoadAction(InLoadAction),
			StencilStoreAction(InStoreAction),
			DepthStencilAccess(ExclusiveDepthStencil::DepthWrite_StencilWrite)
		{
			Validate();
		}

		explicit CGIDepthRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InLoadAction, ERenderTargetStoreAction InStoreAction, ExclusiveDepthStencil InDepthStencilAccess) :
			Texture(InTexture),
			DepthLoadAction(InLoadAction),
			DepthStoreAction(InStoreAction),
			StencilLoadAction(InLoadAction),
			StencilStoreAction(InStoreAction),
			DepthStencilAccess(InDepthStencilAccess)
		{
			Validate();
		}

		explicit CGIDepthRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InDepthLoadAction, ERenderTargetStoreAction InDepthStoreAction, ERenderTargetLoadAction InStencilLoadAction, ERenderTargetStoreAction InStencilStoreAction) :
			Texture(InTexture),
			DepthLoadAction(InDepthLoadAction),
			DepthStoreAction(InDepthStoreAction),
			StencilLoadAction(InStencilLoadAction),
			StencilStoreAction(InStencilStoreAction),
			DepthStencilAccess(ExclusiveDepthStencil::DepthWrite_StencilWrite)
		{
			Validate();
		}

		explicit CGIDepthRenderTargetView(CGITexture* InTexture, ERenderTargetLoadAction InDepthLoadAction, ERenderTargetStoreAction InDepthStoreAction, ERenderTargetLoadAction InStencilLoadAction, ERenderTargetStoreAction InStencilStoreAction, ExclusiveDepthStencil InDepthStencilAccess) :
			Texture(InTexture),
			DepthLoadAction(InDepthLoadAction),
			DepthStoreAction(InDepthStoreAction),
			StencilLoadAction(InStencilLoadAction),
			StencilStoreAction(InStencilStoreAction),
			DepthStencilAccess(InDepthStencilAccess)
		{
			Validate();
		}

		void Validate() const
		{
			// VK and Metal MAY leave the attachment in an undefined state if the StoreAction is DontCare. So we can't assume read-only implies it should be DontCare unless we know for sure it will never be used again.
			// ensureMsgf(DepthStencilAccess.IsDepthWrite() || DepthStoreAction == ERenderTargetStoreAction::ENoAction, TEXT("Depth is read-only, but we are performing a store.  This is a waste on mobile.  If depth can't change, we don't need to store it out again"));
			/*ensureMsgf(DepthStencilAccess.IsStencilWrite() || StencilStoreAction == ERenderTargetStoreAction::ENoAction, TEXT("Stencil is read-only, but we are performing a store.  This is a waste on mobile.  If stencil can't change, we don't need to store it out again"));*/
		}

		bool operator==(const CGIDepthRenderTargetView& Other) const
		{
			return
				Texture == Other.Texture &&
				DepthLoadAction == Other.DepthLoadAction &&
				DepthStoreAction == Other.DepthStoreAction &&
				StencilLoadAction == Other.StencilLoadAction &&
				StencilStoreAction == Other.StencilStoreAction &&
				DepthStencilAccess == Other.DepthStencilAccess;
		}
	};
    class CGISetRenderTargetsInfo
	{
	public:
		// Color Render Targets Info
		FRHIRenderTargetView ColorRenderTarget[MaxSimultaneousRenderTargets];	
		int32 NumColorRenderTargets;
		bool bClearColor;

		// Color Render Targets Info
		FRHIRenderTargetView ColorResolveRenderTarget[MaxSimultaneousRenderTargets];	
		bool bHasResolveAttachments;

		// Depth/Stencil Render Target Info
		CGIDepthRenderTargetView DepthStencilRenderTarget;	
		// Used when depth resolve is enabled.
		CGIDepthRenderTargetView DepthStencilResolveRenderTarget;
		bool bClearDepth;
		bool bClearStencil;

		CGITexture* ShadingRateTexture;
		EVRSRateCombiner ShadingRateTextureCombiner;

		uint8 MultiViewCount;

		CGISetRenderTargetsInfo() :
			NumColorRenderTargets(0),
			bClearColor(false),
			bHasResolveAttachments(false),
			bClearDepth(false),
			ShadingRateTexture(nullptr),
			MultiViewCount(0)
		{}

		CGISetRenderTargetsInfo(int32 InNumColorRenderTargets, const FRHIRenderTargetView* InColorRenderTargets, const CGIDepthRenderTargetView& InDepthStencilRenderTarget) :
			NumColorRenderTargets(InNumColorRenderTargets),
			bClearColor(InNumColorRenderTargets > 0 && InColorRenderTargets[0].LoadAction == ERenderTargetLoadAction::EClear),
			bHasResolveAttachments(false),
			DepthStencilRenderTarget(InDepthStencilRenderTarget),		
			bClearDepth(InDepthStencilRenderTarget.Texture && InDepthStencilRenderTarget.DepthLoadAction == ERenderTargetLoadAction::EClear),
			ShadingRateTexture(nullptr),
			ShadingRateTextureCombiner(VRSRB_Passthrough)
		{
			for (int32 Index = 0; Index < InNumColorRenderTargets; ++Index)
			{
				ColorRenderTarget[Index] = InColorRenderTargets[Index];			
			}
		}
		// @todo metal mrt: This can go away after all the cleanup is done
		void SetClearDepthStencil(bool bInClearDepth, bool bInClearStencil = false)
		{
			if (bInClearDepth)
			{
				DepthStencilRenderTarget.DepthLoadAction = ERenderTargetLoadAction::EClear;
			}
			if (bInClearStencil)
			{
				DepthStencilRenderTarget.StencilLoadAction = ERenderTargetLoadAction::EClear;
			}
			bClearDepth = bInClearDepth;		
			bClearStencil = bInClearStencil;		
		}

		uint32 CalculateHash() const
		{
			// Need a separate struct so we can memzero/remove dependencies on reference counts
			struct FHashableStruct
			{
				// *2 for color and resolves
				// depth goes in the third-to-last slot
				// depth resolve goes in the second-to-last slot
				// shading rate goes in the last slot
				CGITexture* Texture[MaxSimultaneousRenderTargets*2 + 3];
				uint32 MipIndex[MaxSimultaneousRenderTargets];
				uint32 ArraySliceIndex[MaxSimultaneousRenderTargets];
				ERenderTargetLoadAction LoadAction[MaxSimultaneousRenderTargets];
				ERenderTargetStoreAction StoreAction[MaxSimultaneousRenderTargets];

				ERenderTargetLoadAction		DepthLoadAction;
				ERenderTargetStoreAction	DepthStoreAction;
				ERenderTargetLoadAction		StencilLoadAction;
				ERenderTargetStoreAction	StencilStoreAction;
				ExclusiveDepthStencil		DepthStencilAccess;

				bool bClearDepth;
				bool bClearStencil;
				bool bClearColor;
				bool bHasResolveAttachments;
				//FRHIUnorderedAccessView* UnorderedAccessView[MaxSimultaneousUAVs];
				uint8 MultiViewCount;

				void Set(const CGISetRenderTargetsInfo& RTInfo)
				{
					//FMemory::Memzero(*this);
					for (int32 Index = 0; Index < RTInfo.NumColorRenderTargets; ++Index)
					{
						Texture[Index] = RTInfo.ColorRenderTarget[Index].Texture;
						Texture[MaxSimultaneousRenderTargets+Index] = RTInfo.ColorResolveRenderTarget[Index].Texture;
						MipIndex[Index] = RTInfo.ColorRenderTarget[Index].MipIndex;
						ArraySliceIndex[Index] = RTInfo.ColorRenderTarget[Index].ArraySliceIndex;
						LoadAction[Index] = RTInfo.ColorRenderTarget[Index].LoadAction;
						StoreAction[Index] = RTInfo.ColorRenderTarget[Index].StoreAction;
					}

					Texture[MaxSimultaneousRenderTargets*2] = RTInfo.DepthStencilRenderTarget.Texture;
					Texture[MaxSimultaneousRenderTargets*2 + 1] = RTInfo.DepthStencilResolveRenderTarget.Texture;
					Texture[MaxSimultaneousRenderTargets*2 + 2] = RTInfo.ShadingRateTexture;
					DepthLoadAction = RTInfo.DepthStencilRenderTarget.DepthLoadAction;
					DepthStoreAction = RTInfo.DepthStencilRenderTarget.DepthStoreAction;
					StencilLoadAction = RTInfo.DepthStencilRenderTarget.StencilLoadAction;
					StencilStoreAction = RTInfo.DepthStencilRenderTarget.GetStencilStoreAction();
					DepthStencilAccess = RTInfo.DepthStencilRenderTarget.GetDepthStencilAccess();

					bClearDepth = RTInfo.bClearDepth;
					bClearStencil = RTInfo.bClearStencil;
					bClearColor = RTInfo.bClearColor;
					bHasResolveAttachments = RTInfo.bHasResolveAttachments;
					MultiViewCount = RTInfo.MultiViewCount;
				}
			};

			FHashableStruct RTHash;
			//FMemory::Memzero(RTHash);
			RTHash.Set(*this);
			return 0;//FCrc::MemCrc32(&RTHash, sizeof(RTHash));
		}
	};
	struct CGIRenderPassInfo
	{
		struct FColorEntry
		{
			CGITexture*         RenderTarget      = nullptr;
			CGITexture*         ResolveTarget     = nullptr;
			int32                ArraySlice        = -1;
			uint8                MipIndex          = 0;
			ERenderTargetActions Action            = ERenderTargetActions::DontLoad_DontStore;
		};
		FColorEntry mColorRenderTargets;
		struct FDepthStencilEntry
		{
			CGITexture*         DepthStencilTarget = nullptr;
			CGITexture*         ResolveTarget      = nullptr;
			EDepthStencilTargetActions Action       = EDepthStencilTargetActions::DontLoad_DontStore;
			//FExclusiveDepthStencil ExclusiveDepthStencil;
		};
		FDepthStencilEntry DepthStencilRenderTarget;
		bool mbIsSwapChain;
		uint32 mbSwapChainImageIndex;
		void ConvertToRenderTargetsInfo(CGISetRenderTargetsInfo& OutRTInfo) const;
	};
	class CGIBuffer:public CGIResource
	{
	public:
		CGIBufferDesc Desc;
		CGIBuffer(CGIBufferDesc const& InDesc)
		: CGIResource(RRT_Buffer)//, ERHIAccess::Unknown /* TODO (RemoveUnknowns): Use InitialAccess from descriptor after refactor. */)
		, Desc(InDesc)
		{}
		/** @return The number of bytes in the buffer. */
		uint32 GetSize() const { return Desc.Size; }

		/** @return The stride in bytes of the buffer. */
		uint32 GetStride() const { return Desc.Stride; }

		/** @return The usage flags used to create the buffer. */
		EBufferUsageFlags GetUsage() const { return Desc.Usage; }
	};
	class CGIShader : public CGIResource
	{
	public:
		CGIShader(ECGIResourceType InResourceType, EShaderFrequency InFrequency)
			:CGIResource(InResourceType)
		{
			
		}
	};
	class CGIGraphicsShader : public CGIShader
	{
	public:
		explicit CGIGraphicsShader(ECGIResourceType InResourceType, EShaderFrequency InFrequency)
			: CGIShader(InResourceType, InFrequency) {}
	};
	class CGIVertexShader : public CGIGraphicsShader
	{
	public:
		CGIVertexShader() : CGIGraphicsShader(RRT_VertexShader, SF_Vertex) {}
	};
	class CGIPixelShader : public CGIGraphicsShader
	{
	public:
		CGIPixelShader() : CGIGraphicsShader(RRT_PixelShader, SF_Pixel) {}
	};
	class CGIGraphicsPipelineState : public CGIResource 
	{
	public:
		CGIGraphicsPipelineState() : CGIResource(RRT_GraphicsPipelineState) {}

		inline void SetSortKey(uint64 InSortKey) { SortKey = InSortKey; }
		inline uint64 GetSortKey() const { return SortKey; }

		virtual CGIGraphicsShader* GetShader(EShaderFrequency Frequency) const = 0;

	private:
		uint64 SortKey = 0;

#if ENABLE_RHI_VALIDATION
		friend class FValidationContext;
		friend class FValidationRHI;
		FExclusiveDepthStencil DSMode;
#endif
	};
	class CGIComputePipelineState : public CGIResource
	{
	public:
		CGIComputePipelineState() : CGIResource(RRT_ComputePipelineState){
		}
	};
	struct FBoundShaderStateInput
	{
		inline FBoundShaderStateInput():mVertexDeclaration(nullptr),mVertexShader(nullptr),mPixelShader(nullptr) {}
		inline FBoundShaderStateInput
		(
			CGIVertexDeclaration* InVertexDeclarationRHI
			, CGIShader* InVertexShaderRHI
			, CGIShader* InPixelShaderRHI
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
			, FRHIGeometryShader* InGeometryShaderRHI
	#endif
		)
			: mVertexDeclaration(InVertexDeclarationRHI)
			, mVertexShader(InVertexShaderRHI)
			, mPixelShader(InPixelShaderRHI)
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
			, GeometryShaderRHI(InGeometryShaderRHI)
	#endif
		{
		}
		CGIVertexDeclaration* mVertexDeclaration;
		CGIShader* mVertexShader;
		CGIShader* mPixelShader;
	};
	class FGraphicsPipelineStateInitializer
	{
	public:
		FBoundShaderStateInput			mBoundShaderStateInput;
		CGIBlendState*					mCGIBlendState;
		CGIRasterizerState*				RasterizerState;
		CGIDepthStencilState*			DepthStencilState;
		FImmutableSamplerState			ImmutableSamplerState;
		EPrimitiveType					PrimitiveType;
		uint32							RenderTargetsEnabled;
		std::vector<EPixelFormat>		RenderTargetFormats;
		std::vector<ETextureCreateFlags> RenderTargetFlags;
		EPixelFormat					DepthStencilTargetFormat;
		ETextureCreateFlags				DepthStencilTargetFlag;
		ERenderTargetLoadAction			DepthTargetLoadAction;
		ERenderTargetStoreAction		DepthTargetStoreAction;
		ERenderTargetLoadAction			StencilTargetLoadAction;
		ERenderTargetStoreAction		StencilTargetStoreAction;
		ExclusiveDepthStencil			DepthStencilAccess;
		uint16							NumSamples;
		ESubpassHint					SubpassHint;
		uint8							SubpassIndex;
		EConservativeRasterization		ConservativeRasterization;
		bool							bDepthBounds;
		uint8							MultiViewCount;
		bool							bHasFragmentDensityAttachment;
		bool							bAllowVariableRateShading;
		EVRSShadingRate					ShadingRate;
		// Note: these flags do NOT affect compilation of this PSO.
		// The resulting object is invariant with respect to whatever is set here, they are
		// behavior hints.
		// They do not participate in equality comparisons or hashing.
		union
		{
			struct
			{
				uint16					Reserved			: 11;
				uint16					bPSOPrecache		: 1;
				uint16					bFromPSOFileCache	: 1;
				uint16					PrecacheCompileType : 3;
			};
			uint16						Flags;
		};
		enum class EPSOPrecacheCompileType : uint8
		{
			NotSet = 0,
			MinPri = 1,
			NormalPri = 2,
			MaxPri = 3,

			NumTypes = 4,
		};
		static_assert((int)EPSOPrecacheCompileType::MaxPri < (1<<3) ); // ensure MaxPri fits within PrecacheCompileType
		void SetPSOPrecacheCompileType(EPSOPrecacheCompileType PrecacheCompileTypeIN) 
		{ 
			check(PrecacheCompileTypeIN <= EPSOPrecacheCompileType::MaxPri && PrecacheCompileTypeIN >= EPSOPrecacheCompileType::MinPri);
			PrecacheCompileType = (uint16)PrecacheCompileTypeIN;
		}
		EPSOPrecacheCompileType GetPSOPrecacheCompileType() const {	return (EPSOPrecacheCompileType)PrecacheCompileType; }
		// Cached hash off all state data provided at creation time (Only contains hash of data which influences the PSO precaching for the current platform)
		// Created from hashing the state data instead of the pointers which are used during fast runtime cache checking and compares
		uint64							StatePrecachePSOHash;
		FGraphicsPipelineStateInitializer()
		: mCGIBlendState(nullptr)
		, RasterizerState(nullptr)
		, DepthStencilState(nullptr)
		, RenderTargetsEnabled(0)
		, RenderTargetFormats(InPlace, EPixelFormat::PF_Unknown)
		, RenderTargetFlags(InPlace, ETextureCreateFlags::None)
		, DepthStencilTargetFormat(EPixelFormat::PF_Unknown)
		, DepthStencilTargetFlag(ETextureCreateFlags::None)
		, DepthTargetLoadAction(ERenderTargetLoadAction::ENoAction)
		, DepthTargetStoreAction(ERenderTargetStoreAction::ENoAction)
		, StencilTargetLoadAction(ERenderTargetLoadAction::ENoAction)
		, StencilTargetStoreAction(ERenderTargetStoreAction::ENoAction)
		, NumSamples(0)
		, SubpassHint(ESubpassHint::None)
		, SubpassIndex(0)
		, ConservativeRasterization(EConservativeRasterization::Disabled)
		, bDepthBounds(false)
		, MultiViewCount(0)
		, bHasFragmentDensityAttachment(false)
		, bAllowVariableRateShading(true)
		, ShadingRate(EVRSShadingRate::VRSSR_1x1)
		, Flags(0)
		, StatePrecachePSOHash(0)
		{
#if PLATFORM_WINDOWS
			static_assert(sizeof(TRenderTargetFormats::ElementType) == sizeof(uint8/*EPixelFormat*/), "Change TRenderTargetFormats's uint8 to EPixelFormat's size!");
#endif
			static_assert(PF_MAX < MAX_uint8, "TRenderTargetFormats assumes EPixelFormat can fit in a uint8!");
		}
		FGraphicsPipelineStateInitializer(
			FBoundShaderStateInput		InBoundShaderState,
			CGIBlendState*				InBlendState,
			CGIRasterizerState*		InRasterizerState,
			CGIDepthStencilState*		InDepthStencilState,
			FImmutableSamplerState		InImmutableSamplerState,
			EPrimitiveType				InPrimitiveType,
			uint32						InRenderTargetsEnabled,
			const std::vector<EPixelFormat>&	InRenderTargetFormats,
			const std::vector<ETextureCreateFlags>&	InRenderTargetFlags,
			EPixelFormat				InDepthStencilTargetFormat,
			ETextureCreateFlags			InDepthStencilTargetFlag,
			ERenderTargetLoadAction		InDepthTargetLoadAction,
			ERenderTargetStoreAction	InDepthTargetStoreAction,
			ERenderTargetLoadAction		InStencilTargetLoadAction,
			ERenderTargetStoreAction	InStencilTargetStoreAction,
			ExclusiveDepthStencil		InDepthStencilAccess,
			uint16						InNumSamples,
			ESubpassHint				InSubpassHint,
			uint8						InSubpassIndex,
			EConservativeRasterization	InConservativeRasterization,
			uint16						InFlags,
			bool						bInDepthBounds,
			uint8						InMultiViewCount,
			bool						bInHasFragmentDensityAttachment,
			bool						bInAllowVariableRateShading,
			EVRSShadingRate				InShadingRate)
		: mBoundShaderStateInput(InBoundShaderState)
		, mCGIBlendState(InBlendState)
		, RasterizerState(InRasterizerState)
		, DepthStencilState(InDepthStencilState)
		, ImmutableSamplerState(InImmutableSamplerState)
		, PrimitiveType(InPrimitiveType)
		, RenderTargetsEnabled(InRenderTargetsEnabled)
		, RenderTargetFormats(InRenderTargetFormats)
		, RenderTargetFlags(InRenderTargetFlags)
		, DepthStencilTargetFormat(InDepthStencilTargetFormat)
		, DepthStencilTargetFlag(InDepthStencilTargetFlag)
		, DepthTargetLoadAction(InDepthTargetLoadAction)
		, DepthTargetStoreAction(InDepthTargetStoreAction)
		, StencilTargetLoadAction(InStencilTargetLoadAction)
		, StencilTargetStoreAction(InStencilTargetStoreAction)
		, DepthStencilAccess(InDepthStencilAccess)
		, NumSamples(InNumSamples)
		, SubpassHint(InSubpassHint)
		, SubpassIndex(InSubpassIndex)
		, ConservativeRasterization(EConservativeRasterization::Disabled)
		, bDepthBounds(bInDepthBounds)
		, MultiViewCount(InMultiViewCount)
		, bHasFragmentDensityAttachment(bInHasFragmentDensityAttachment)
		, bAllowVariableRateShading(bInAllowVariableRateShading)
		, ShadingRate(InShadingRate)
		, Flags(InFlags)
		, StatePrecachePSOHash(0)
	{
	}
	};
    class CGIViewport : public CGIResource{
    public:
        CGIViewport() : CGIResource(RRT_Viewport) {}
        /**
         * Returns access to the platform-specific native resource pointer.  This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeSwapChain() const { return nullptr; }
        /**
         * Returns access to the platform-specific native resource pointer to a backbuffer texture.  This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeBackBufferTexture() const { return nullptr; }
        /**
         * Returns access to the platform-specific native resource pointer to a backbuffer rendertarget. This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeBackBufferRT() const { return nullptr; }

        /**
         * Returns access to the platform-specific native window. This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all. 
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason.
         * AddParam could represent any additional platform-specific data (could be null).
         */
        virtual void* GetNativeWindow(void** AddParam = nullptr) const { return nullptr; }

        /**
         * Sets custom Present handler on the viewport
         */
        virtual void SetCustomPresent(class FRHICustomPresent*) {}

        /**
         * Returns currently set custom present handler.
         */
        virtual class FRHICustomPresent* GetCustomPresent() const { return nullptr; }


        /**
         * Ticks the viewport on the Game thread
         */
        virtual void Tick(float DeltaTime) {}

        virtual void WaitForFrameEventCompletion() { }

        virtual void IssueFrameEvent() { }
        void Draw( bool bShouldPresent = true );
    };
}
